﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows.Forms;

namespace LittleBee.Utils
{
	public class MultiInputBox
	{
		/// <summary>
		/// 多项输入控件对话框
		/// Json array format:
		///	{
		///		name:'',
		///		type:'',
		///		hit:'',
		///		default:'',
		///		params: {
		///			...
		///		},
		///		rules: [
		///		{ OnExit:'not empty' },
		///		{ ErrorHit:'Cell名称是空' },
		///			...
		///		],
		///		##type=combo时,
		///		list:[{key:'', value:''},...]
		///	}
		/// </summary>
		/// <param name="strCaption">标题</param>
		/// <param name="jaInputs">以json数组来输入各个控件参数</param>
		/// <param name="strReturns">返回值列表</param>
		/// <returns></returns>
		public static bool InputBox(string strCaption, JArray jaInputs, out JObject joReturns, int nWidth = 320)
		{
			const int nLineHeight = 28;
			bool blRet = false;
			//joReturns = null;

			Form InputForm = new Form();

			InputForm.SuspendLayout();
			InputForm.MinimizeBox = false;
			InputForm.MaximizeBox = false;
			InputForm.StartPosition = FormStartPosition.CenterScreen;
			InputForm.FormBorderStyle = FormBorderStyle.FixedDialog;
			InputForm.ClientSize = new Size(nWidth, (jaInputs.Count + 2) * nLineHeight);
			InputForm.Text = strCaption;

			TableLayoutPanel table = new TableLayoutPanel();
			table.Padding = new Padding(8, 0, 8, 0);
			table.Parent = InputForm;
			table.ColumnCount = 3;
			table.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 0.3f));
			table.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 0.35f));
			table.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 0.35f));
			table.Dock = System.Windows.Forms.DockStyle.Fill;
			table.Location = new System.Drawing.Point(0, 0);
			table.Name = "tableLayoutPanel1";
			table.RowCount = jaInputs.Count + 2;
			table.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.AutoSize));
			table.Controls.Add(new Label(), 0, 0);

			Dictionary<string, Control> dctControls = new Dictionary<string, Control>();
			int nRow = 0;
			foreach (JObject joInput in jaInputs)
			{
				string strName = joInput.Value<string>("name")!;
				string strType = joInput.Value<string>("type")!;
				string strHit = joInput.Value<string>("hit")!;
				string strDefault = joInput.Value<string>("default")!;

				Label lbl = new Label();
				lbl.Text = strHit;
				lbl.AutoSize = true;
				lbl.TextAlign = ContentAlignment.MiddleRight;
				lbl.Dock = DockStyle.Right;
				table.Controls.Add(lbl, 0, nRow + 1);

				Control control;
				switch (strType)
				{
					case "text":
						{
							TextBox tb = new TextBox();
							tb.Name = strName;
							tb.Text = strDefault;
							tb.SelectAll();
							tb.Dock = DockStyle.Fill;
							table.Controls.Add(tb, 1, nRow + 1);
							table.SetColumnSpan(tb, 2);
							control = tb;
						}
						break;
					case "combo":
						{
							ComboBox cb = new ComboBox();
							cb.Name = strName;

							Dictionary<string, string> _dctKeyValue = new Dictionary<string, string>();
							foreach(var item in joInput["list"]!.ToArray())
							{
								_dctKeyValue[item.Value<string>("key")!] = item.Value<string>("value")!;
							}
							cb.DataSource = _dctKeyValue.ToArray();
							cb.DisplayMember = "key";
							cb.ValueMember = "value";
							//cb.Items.AddRange(joInput["list"].Values<string>().ToArray());
							//cb.SelectedIndex = 0;
							cb.Dock = DockStyle.Fill;
							table.Controls.Add(cb, 1, nRow + 1);
							table.SetColumnSpan(cb, 2);
							control = cb;
						}
						break;
					default:
						throw new NotSupportedException($"not support control type: {strType}");
				}

				if (joInput.ContainsKey("rules"))
				{
					control.Tag = joInput["rules"];
				}

				dctControls[strName] = control;
				++nRow;
			}

			InputForm.FormClosing += OnInputFormClosing!;
			InputForm.Tag = jaInputs;

			Button btnok = new Button();
			btnok.Text = "确定";
			InputForm.AcceptButton = btnok;//回车响应
			table.Controls.Add(btnok, 1, jaInputs.Count + 1);

			btnok.DialogResult = DialogResult.OK;
			Button btncancal = new Button();
			btncancal.Text = "取消";
			btncancal.DialogResult = DialogResult.Cancel;
			table.Controls.Add(btncancal, 2, jaInputs.Count + 1);

			InputForm.ResumeLayout();

			try
			{
				joReturns = new JObject();
				if (InputForm.ShowDialog() == DialogResult.OK)
				{
					//tupRet = new Tuple<string, string>(tb1.Text, tb2.Text);
					foreach (var item in dctControls)
					{
						switch (item.Value.GetType().ToString())
						{
							case "System.Windows.Forms.TextBox":
								joReturns[item.Key] = (item.Value as TextBox)!.Text;
								break;
							case "System.Windows.Forms.ComboBox":
								joReturns[item.Key] = (item.Value as ComboBox)!.SelectedValue.ToString();
								break;
							default:
								throw new ApplicationException($"Unhandled MultiInputBox control: {item.Value.GetType().ToString()}");
						}
					}
					blRet = true;
				}
				else
				{
					blRet = false;
				}
			}
			finally
			{
				InputForm.Dispose();
			}

			return blRet;
		}

		private static void OnInputFormClosing(object sender, FormClosingEventArgs e)
		{
			Control control = (sender as Control)!;
			Form frmInput = control.FindForm() as Form;
			if (frmInput.DialogResult == DialogResult.Cancel) return;

			JArray jaInputs = (frmInput.Tag as JArray)!;

			try
			{
				foreach (JObject joInput in jaInputs)
				{
					string strType = joInput.Value<string>("type")!;
					string strName = joInput.Value<string>("name")!;
					if (joInput.ContainsKey("rules") == false) continue;

					JArray jaRules = (joInput["rules"] as JArray)!;
					switch (strType)
					{
						case "text":
							{
								TextBox tb = (frmInput.Controls[0].Controls.Find(strName, true)[0] as TextBox)!;
								foreach (JObject joRule in jaRules)
								{
									if (joRule.ContainsKey("OnExit"))
									{
										string strExitRule = joRule.Value<string>("OnExit")!;
										string strErrorHit = joRule.Value<string>("ErrorHit")!;
										if (strExitRule == "NotEmpty")
										{
											if (string.IsNullOrEmpty(tb.Text) == true)
											{
												tb.Focus();
												throw new ValidateRuleException(strErrorHit);
											}
										}
									}
								}
							}
							break;
						default:
							break;
					}
				}
			}
			catch (ValidateRuleException ex)
			{
				MessageBox.Show(ex.Message, frmInput.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
				e.Cancel = true;
			}
		}
	}

	[Serializable]
	internal class ValidateRuleException : Exception
	{
		public ValidateRuleException()
		{
		}

		public ValidateRuleException(string message) : base(message)
		{
		}

		public ValidateRuleException(string message, Exception innerException) : base(message, innerException)
		{
		}

		protected ValidateRuleException(SerializationInfo info, StreamingContext context) : base(info, context)
		{
		}
	}
}
